package com.luqili.utils.pub.rest.param;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import com.luqili.utils.pub.base.LuUserInputException;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * 通用请求参数,引入项目后应继承使用
 *
 * @author luqili
 */
public abstract class LuParams extends HashMap<String, Object> {

    /**
     * 将空字符串转换为Null
     *
     * @param fieldName 指定字段名称
     */
    public void setEmptyToNull(String fieldName) {
        if (this.get(fieldName) != null && StringUtils.isBlank(this.get(fieldName).toString())) {
            this.put(fieldName, null);
        }
    }

    /**
     * 将所有字段的空字符串转换为Null
     */
    public void setEmptyToNull() {
        for (String key : this.keySet()) {
            this.setEmptyToNull(key);
        }
    }

    /**
     * 获得一个字符串
     *
     * @param key
     * @return
     */
    public String getStr(String key) {
        return MapUtil.getStr(this, key);
    }

    /**
     * 自动截取指定长度的字符串
     *
     * @param key
     * @param len
     * @return
     */
    public String getStrLen(String key, int len) {
        String str = getStr(key);
        return StringUtils.left(str, len);
    }

    /**
     * 获取指定字符
     *
     * @param key
     * @param len
     * @param msgPrefix  提示信息前缀
     * @param allowEmpty 为空时抛出异常
     * @return
     */
    public String getStrMaxLen(String key, int len, String msgPrefix, boolean allowEmpty) {
        String str = getStr(key);
        if (!allowEmpty) {
            if (StringUtils.isBlank(str)) {
                throw new LuUserInputException(msgPrefix + "不允许为空");
            }
        }
        if (str != null && str.length() > len) {
            throw new LuUserInputException(msgPrefix + "不能超过" + len + "个字符");
        }
        return str;
    }

    /**
     * 获取值
     *
     * @param key          字段名
     * @param defaultValue 默认值
     * @return 获取的值
     */
    public String getStr(String key, String defaultValue) {
        return MapUtil.getStr(this, key, defaultValue);
    }

    /**
     * 获取值
     *
     * @param key 字段名
     * @return 获取的值
     */
    public List<String> getListStr(String key) {
        return MapUtil.get(this, key, new TypeReference<ArrayList<String>>() {
        });
    }

    /**
     *
     * @param key
     * @param len
     * @param allowEmpty 是否允许为空
     * @return
     */
    public String getEmail(String key, int len, boolean allowEmpty) {
        String email = getStrMaxLen(key, len, "电子邮箱", allowEmpty);
        if (StrUtil.isNotBlank(email)) {
            if (!Validator.isEmail(email)) {
                throw new LuUserInputException("电子邮箱格式错误");
            }
        }
        return email;
    }

    /**
     * 获取并验证是否为手机号码（中国）
     *
     * @param key
     * @param allowEmpty 是否允许为空
     * @return
     */
    public String getMobile(String key, boolean allowEmpty) {
        String mobile = getStrMaxLen(key, 18, "手机号", allowEmpty);
        if (StrUtil.isNotBlank(mobile)) {
            if (!PhoneUtil.isMobile(mobile)) {
                throw new LuUserInputException("手机号格式错误");
            }
        }
        return mobile;
    }

    /**
     * 获取并验证是否为身份证
     *
     * @param key
     * @param allowEmpty 是否允许为空
     * @return
     */
    public String getIdCard(String key, boolean allowEmpty) {
        String idcard = getStrMaxLen(key, 18, "身份证", allowEmpty);
        if (StrUtil.isNotBlank(idcard)) {
            if (!IdcardUtil.isValidCard18(idcard)) {
                throw new LuUserInputException("身份证格式错误");
            }
        }
        return idcard;
    }

    /**
     * 获取值
     *
     * @param key 字段名
     * @return 获取的值
     */
    public List<Integer> getListInt(String key) {
        return MapUtil.get(this, key, new TypeReference<ArrayList<Integer>>() {
        });
    }

    /**
     * 获取值
     *
     * @param key 字段名
     * @return 获取的值
     */
    public List<Long> getListLong(String key) {
        return MapUtil.get(this, key, new TypeReference<ArrayList<Long>>() {
        });
    }

    /**
     * 获取值
     *
     * <p>支持数组形式,支持字符串转数据 日期分隔符 ~</p>
     * <p>如:["2021-01-01 12:00","2021-01-01 13:00","2021-01-02 13:00"]</p>
     * <p>如:"2021-01-01 12:00 ~ 2021-01-02 12:00 ~ 2021-01-03 14:00"</p>
     *
     * @param key 字段名
     * @return 获取的值
     */
    public List<Date> getListDate(String key) {
        try {
            return MapUtil.get(this, key, new TypeReference<ArrayList<Date>>() {
            });
        } catch (Exception e) {
            String str = MapUtil.getStr(this, key);
            List<String> lines = StrUtil.split(str, "~");
            List<Date> datas = new ArrayList<>();
            for (String line : lines) {
                try {
                    Date d = DateUtil.parse(line);
                    if (d != null) {
                        datas.add(d);
                    }
                } catch (Exception e2) {
                }
            }
            return datas;
        }
    }

    /**
     * 获取任意类型
     *
     * @param key
     * @param <K,V>
     * @return
     */
    public <K, V> Map<K, V> getMap(String key) {
        return MapUtil.get(this, key, new TypeReference<HashMap<K, V>>() {
        });
    }

    /**
     * 获取任意类型
     *
     * @param key
     * @param type
     * @param <T>
     * @return
     */
    public <T> T getObj(String key, Class<T> type) {
        return MapUtil.get(this, key, type);
    }

    /**
     * 获取任意类型的List集合
     *
     * @param key
     * @param <T>
     * @return
     */
    public <T> List<T> getListObj(String key) {
        return MapUtil.get(this, key, new TypeReference<ArrayList<T>>() {
        });
    }


    /**
     * 获得值
     *
     * @param key 字段名
     * @return 获取值
     */
    public Boolean getBoolean(String key) {
        return MapUtil.getBool(this, key);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param defaultVal 默认值，为空返回空值
     * @return 获取的值
     */
    public Boolean getBoolean(String key, Boolean defaultVal) {
        return MapUtil.getBool(this, key, defaultVal);
    }

    /**
     * 获取值
     *
     * @param key       字段名
     * @param msgPrefix 提示名称，字段名称
     * @return 获取的值
     */
    public Boolean getBooleanNotNull(String key, String msgPrefix) {
        return getValue(key, Boolean.class, msgPrefix, false, null, null, null);
    }

    /**
     * 获得一个Byte
     *
     * @param key 字段名
     * @return 获取值
     */
    public Byte getByte(String key) {
        return getByte(key, null);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param defaultVal 默认值，为空返回空值
     * @return 获取的值
     */
    public Byte getByte(String key, Byte defaultVal) {
        Short v = getShort(key);
        return v != null ? v.byteValue() : defaultVal;
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param msgPrefix  提示名称，字段名称
     * @param allowNull  是否允许为空
     * @param defaultVal 默认值，为空返回空值
     * @param min        最小值，为空不验证
     * @param max        最大值，为空不验证
     * @return 获取值
     */
    public Byte getByte(String key, String msgPrefix, boolean allowNull, Byte defaultVal, Byte min, Byte max) {
        return getValue(key, Byte.class, msgPrefix, false, defaultVal, min, max);
    }

    /**
     * 获取值
     *
     * @param key       字段名
     * @param msgPrefix 提示名称，字段名称
     * @return 获取的值
     */
    public Byte getByteNotNull(String key, String msgPrefix) {
        return getValue(key, Byte.class, msgPrefix, false, null, null, null);
    }

    /**
     * 获取值
     *
     * @param key 字段名
     * @return 获取的值
     */
    public Short getShort(String key) {
        return MapUtil.getShort(this, key);
    }

    /**
     * 获取值
     *
     * @param key 字段名
     * @return 获取的值
     */
    public Short getShort(String key, Short defaultValue) {
        return MapUtil.getShort(this, key, defaultValue);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param msgPrefix  提示名称，字段名称
     * @param allowNull  是否允许为空
     * @param defaultVal 默认值，为空返回空值
     * @param min        最小值，为空不验证
     * @param max        最大值，为空不验证
     * @return
     */
    public Short getShort(String key, String msgPrefix, boolean allowNull, Short defaultVal, Short min, Short max) {
        return getValue(key, Short.class, msgPrefix, allowNull, defaultVal, min, max);
    }

    /**
     * 获取值
     *
     * @param key       字段名
     * @param msgPrefix 提示名称，字段名称
     * @return 获取的值
     */
    public Short getShortNotNull(String key, String msgPrefix) {
        return getValue(key, Short.class, msgPrefix, false, null, null, null);
    }

    /**
     * 获得一个Int
     *
     * @param key 字段名
     * @return
     */
    public Integer getInt(String key) {
        return MapUtil.getInt(this, key);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param defaultVal 默认值，为空返回空值
     * @return 获取的值
     */
    public Integer getInt(String key, Integer defaultVal) {
        return MapUtil.getInt(this, key, defaultVal);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param msgPrefix  提示名称，字段名称
     * @param allowNull  是否允许为空
     * @param defaultVal 默认值，为空返回空值
     * @param min        最小值，为空不验证
     * @param max        最大值，为空不验证
     * @return
     */
    public Integer getInt(String key, String msgPrefix, boolean allowNull, Integer defaultVal, Integer min, Integer max) {
        return getValue(key, Integer.class, msgPrefix, allowNull, defaultVal, min, max);
    }

    /**
     * 获取值
     *
     * @param key       字段名
     * @param msgPrefix 提示名称，字段名称
     * @return 获取的值
     */
    public Integer getIntNotNull(String key, String msgPrefix) {
        return getValue(key, Integer.class, msgPrefix, false, null, null, null);
    }

    /**
     * 获得一个Long
     *
     * @param key 字段名
     * @return 获取值
     */
    public Long getLong(String key) {
        return MapUtil.getLong(this, key);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param defaultVal 默认值，为空返回空值
     * @return 获取的值
     */
    public Long getLong(String key, Long defaultVal) {
        return MapUtil.getLong(this, key, defaultVal);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param msgPrefix  提示名称，字段名称
     * @param allowNull  是否允许为空
     * @param defaultVal 默认值，为空返回空值
     * @param min        最小值，为空不验证
     * @param max        最大值，为空不验证
     * @return 获取值
     */
    public Long getLong(String key, String msgPrefix, boolean allowNull, Long defaultVal, Long min, Long max) {
        return getValue(key, Long.class, msgPrefix, allowNull, defaultVal, min, max);
    }

    /**
     * 获取值
     *
     * @param key       字段名
     * @param msgPrefix 提示名称，字段名称
     * @return 获取的值
     */
    public Long getLongNotNull(String key, String msgPrefix) {
        return getValue(key, Long.class, msgPrefix, false, null, null, null);
    }


    /**
     * 获取值
     *
     * @param key 字段名
     * @return 获取的值
     */
    public BigDecimal getBigDecimal(String key) {
        return MapUtil.get(this, key, BigDecimal.class);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param defaultVal 默认值，为空返回空值
     * @return 获取的值
     */
    public BigDecimal getBigDecimal(String key, BigDecimal defaultVal) {
        return MapUtil.get(this, key, BigDecimal.class, defaultVal);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param msgPrefix  提示名称，字段名称
     * @param allowNull  是否允许为空
     * @param defaultVal 默认值，为空返回空值
     * @param min        最小值，为空不验证
     * @param max        最大值，为空不验证
     * @return 获取值
     */
    public BigDecimal getBigDecimal(String key, String msgPrefix, boolean allowNull, BigDecimal defaultVal, BigDecimal min, BigDecimal max) {
        return getValue(key, BigDecimal.class, msgPrefix, allowNull, defaultVal, min, max);
    }

    /**
     * 获取值
     *
     * @param key       字段名
     * @param msgPrefix 提示名称，字段名称
     * @return 获取的值
     */
    public BigDecimal getBigDecimalNotNull(String key, String msgPrefix) {
        return getValue(key, BigDecimal.class, msgPrefix, false, null, null, null);
    }

    /**
     * 获取值
     *
     * @param key       字段名
     * @param msgPrefix 提示名称，字段名称
     * @param min       最小值，为空不验证
     * @param max       最大值，为空不验证
     * @return 获取的值
     */
    public BigDecimal getBigDecimalNotNull(String key, String msgPrefix, BigDecimal min, BigDecimal max) {
        return getValue(key, BigDecimal.class, msgPrefix, false, null, min, max);
    }


    /**
     * 获取值
     *
     * @param key 字段名
     * @return 获取的值
     */
    public Date getDate(String key) {
        return getDate(key, null);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param defaultVal 默认值，为空返回空值
     * @return 获取的值
     */
    public Date getDate(String key, Date defaultVal) {
        if (StrUtil.isBlank(MapUtil.getStr(this, key))) {
            return defaultVal;
        }
        return MapUtil.getDate(this, key, defaultVal);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param msgPrefix  提示名称，字段名称
     * @param allowNull  是否允许为空
     * @param defaultVal 默认值，为空返回空值
     * @param min        最小值，为空不验证
     * @param max        最大值，为空不验证
     * @return 获取值
     */
    public Date getDate(String key, String msgPrefix, boolean allowNull, Date defaultVal, Date min, Date max) {
        return getValue(key, Date.class, msgPrefix, allowNull, defaultVal, min, max);
    }

    /**
     * 获取值
     *
     * @param key       字段名
     * @param msgPrefix 提示名称，字段名称
     * @return 获取的值
     */
    public Date getDateNotNull(String key, String msgPrefix) {
        return getValue(key, Date.class, msgPrefix, false, null, null, null);
    }

    /**
     * 获取值
     *
     * @param key       字段名
     * @param msgPrefix 提示名称，字段名称
     * @param min       最小值，为空不验证
     * @param max       最大值，为空不验证
     * @return 获取的值
     */
    public Date getDateNotNull(String key, String msgPrefix, Date min, Date max) {
        return getValue(key, Date.class, msgPrefix, false, null, min, max);
    }

    /**
     * 获取值
     *
     * @param key        字段名
     * @param type       值的类型
     * @param msgPrefix  提示名称，字段名称
     * @param allowNull  是否允许为空
     * @param defaultVal 默认值，为空返回空值
     * @param min        最小值，为空不验证
     * @param max        最大值，为空不验证
     * @return 获取的值
     */
    private <T> T getValue(String key, Class<T> type, String msgPrefix, boolean allowNull, T defaultVal, T min, T max) {
        T obj = MapUtil.get(this, key, type);
        if (obj == null) {
            if (allowNull) {
                return defaultVal;
            } else {
                throw new LuUserInputException(String.format("%s不能为空", msgPrefix));
            }
        }
        if (min != null || max != null) {
            if (obj instanceof Comparable) {
                Comparable objC = (Comparable) obj;
                if (min != null && objC.compareTo(min) < 0) {
                    throw new LuUserInputException(String.format("%s不能小于%s", msgPrefix, min));
                }
                if (max != null && objC.compareTo(max) > 0) {
                    throw new LuUserInputException(String.format("%s不能大于%s", msgPrefix, max));
                }
            } else {
                throw new LuUserInputException("该数据类型不支持设置最大值或最小值");
            }
        }
        return obj;
    }

    /**
     * 转换模糊搜索功能
     *
     * @param key 模糊查询的值
     * @return 值
     */
    public String getLike(String key) {
        String val = getStr(key);
        return StrUtil.isBlank(val) ? null : "%" + val + "%";
    }

    /**
     * 验证全部为字母时，自动插入 %
     * 便于支持首字母查询
     *
     * @param key 字段名称
     * @return 自动插入%的值
     */
    public String getPinYinSearch(String key) {
        return handPinYinSearch(key);
    }

    /**
     * 验证全部为字母时，自动插入 %
     * 便于支持首字母查询
     *
     * @param key 字段名称
     * @return 自动插入%的值
     */
    public static String handPinYinSearch(String key) {
        if (StringUtils.isNotBlank(key)) {
            StringBuilder sb = new StringBuilder();
            if (Validator.isChinese(key)) {
                sb.append("%").append(key).append("%");
            } else if (StringUtils.isAlpha(key)) {
                for (char c : key.toCharArray()) {
                    sb.append("%").append(c);
                }
                sb.append("%");
            } else {
                sb.append("%").append(key).append("%");
            }
            return sb.toString();
        }
        return null;
    }
}
